/*****************************************************************************
 *   Copyright 2011 ZHUANG Yuan              	                             *
 *   SudokuITS as Final Year Project for BScCS, City University of Hong Kong *
 *                                      									 *
 *   Licensed under the Apache License, Version 2.0 (the "License");         *
 *   you may not use this file except in compliance with the License.        *
 *   You may obtain a copy of the License at                                 *
 *                                                                           *
 *   http://www.apache.org/licenses/LICENSE-2.0                              *
 *                                                                           *
 *   Unless required by applicable law or agreed to in writing, software     *
 *   distributed under the License is distributed on an "AS IS" BASIS,       *
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.*
 *   See the License for the specific language governing permissions and     *
 *   limitations under the License.                                          *
 *****************************************************************************/

package android.sudoku.its.views;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.sudoku.its.util.GameVariables;
import android.sudoku.its.views.R;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

/**
 * Create a Sudoku puzzle game board which is contained in a GamePage
 * @author 51158282
 *
 */

public class GameBoard extends View {
	private final Rect selectedCell = new Rect();
	private float cellWidth;					// width of a single cell
	private float cellHeight;					// height of a single cell
	private float pencilmarkCellWidth;			// width of a sub-cell for a pencil mark within a cell
	private float pencilmarkCellHeight;			// height of a sub-cell for a pencil mark within a cell
	private float coordinateXSpace;				// height of coordinate X (horizontal coordinate)
	private float coordinateYSpace;				// width of coordinate Y (vertical coordinate)
	private float gameboardWidth;				// width of the game board
	private float gameboardHeight;				// height of the game board	
	private int nRow = -1;						// nRow: row number of the selected cell
	private int nCol = -1; 						// nCol: column number of the selected cell
	private int[] clues = new int[GameVariables.PUZZLE_LENGTH];
	private int[][] pencilmarks = new int[GameVariables.PUZZLE_LENGTH][GameVariables.HOUSE_SIZE];	
	
	public GameBoard(Context context, int[] clues, int[][] pencilmarks, int preNRow, int preNCol) {
		super(context);
		setFocusable(true);
		setFocusableInTouchMode(true);
		
		this.clues = clues;
		this.pencilmarks = pencilmarks;
		this.nRow = preNRow;
		this.nCol = preNCol;
	}
	
	@Override
	protected void onSizeChanged(int w, int h, int oldw, int oldh) {
		// Subtract a constant from w and h to leave some space on right and bottom of the game board  
		float tmpW = w - 7f;
		float tmpH = h - 7f; 
		
		// Get the width and the height of a cell
		cellWidth = tmpW/9.5f;
		cellHeight = tmpH/9.5f;
		
		// Get the width and the height of a pencil mark cell
		pencilmarkCellWidth = cellWidth/3f;
		pencilmarkCellHeight = cellHeight/3f;
		
		// Get the space of horizontal and vertical coordinates
		coordinateXSpace = cellHeight * 0.5f;
		coordinateYSpace = cellWidth * 0.5f;

		// Get the width and the height of the game board
		gameboardWidth = tmpW - cellWidth * 0.5f;
		gameboardHeight = tmpH - cellHeight * 0.5f;
		
		// Initialize selected cell
		getSelectedCell();
		Log.d("selected cell", "cooridnate=" + selectedCell.left + ", " + selectedCell.top + ", " + 
				selectedCell.right + ", " + selectedCell.bottom);

		super.onSizeChanged(w, h, oldw, oldh);
	}
	
	@Override
	protected void onDraw(Canvas canvas) {	
		/* Draw the selected cell and houses containing the selected cell */
		
		if (nRow != -1) { // if there is a selected cell
			/* Draw the selected cell */
			
			// Set selected cell style & draw the selected cell
			Paint selectedCellStyle = new Paint();
			selectedCellStyle.setColor(getResources().getColor(R.color.cell_selected));
			canvas.drawRect(selectedCell, selectedCellStyle);	
			
			/* Draw cells that contains the same clue as the selected cell */
			
			int selectedCellValue = clues[nRow * GameVariables.HOUSE_SIZE + nCol];
			for (int i = 0; i < GameVariables.PUZZLE_LENGTH; i++) {
				if ((clues[i] != 0) && (clues[i] == selectedCellValue)) { // find cell with the same clue value as the selected cell and exclude undetermined cells				
					int rowIndex = i/GameVariables.HOUSE_SIZE;
					int colIndex = i%GameVariables.HOUSE_SIZE;
					if ((rowIndex != nRow) && (colIndex != nCol)) { // do not want to draw the selected cell twice
						Rect cellWithSameClue = new Rect();
						cellWithSameClue = getCell(rowIndex, colIndex);
						canvas.drawRect(cellWithSameClue, selectedCellStyle);
					}
				}
			}
			
			/* Draw houses containing the selected cell */
			
			// Set style for houses containing the selected cell and draw the houses
			Paint selectedHouseStyle = new Paint();
			selectedHouseStyle.setColor(getResources().getColor(R.color.house_selected));
			// Draw the row containing the selected cell
			for (int col=0; col < GameVariables.HOUSE_SIZE; col++) {
				if (col != nCol) {	// do not want to redraw the cell that is already drew
					Rect cell = new Rect();
					cell = getCell(nRow, col);				
					canvas.drawRect(cell, selectedHouseStyle);		
				}
			}
			// Draw the column containing the selected cell
			for (int row=0; row < GameVariables.HOUSE_SIZE; row++) {
				if ((row != nRow)) { 	// do not want to redraw the cell that is already drew
					Rect cell = new Rect();
					cell = getCell(row, nCol);
					canvas.drawRect(cell, selectedHouseStyle);	
				}
			}
			// Draw the block containing the selected cell
			int startBlockRow = (nRow/3)*3; 
			int startBlockCol = (nCol/3)*3;
			for (int row=startBlockRow; row<(startBlockRow+GameVariables.BLOCK_SIDE_LENGTH); row++)  
				for (int col=startBlockCol; col<(startBlockCol+GameVariables.BLOCK_SIDE_LENGTH); col++) {
					if ((row != nRow) && (col != nCol)) { 	// do not want to redraw the cell that is already drew
						Rect cell = new Rect();
						cell = getCell(row, col);
						canvas.drawRect(cell, selectedHouseStyle);
					}
				}
		}
				
		/* Draw lines */
		
		// Set line color
		Paint darkColor = new Paint();
		darkColor.setColor(getResources().getColor(R.color.gameboard_darkline));
		
		Paint lightColor = new Paint();
		lightColor.setColor(getResources().getColor(R.color.gameboard_lightline));
		
		// Draw lines
		for (int i=0; i<10; i++) {
			// Draw horizontal lines
			canvas.drawLine(coordinateYSpace, cellHeight * i + coordinateXSpace, 
					gameboardWidth + coordinateYSpace, cellHeight * i + coordinateXSpace, lightColor);
			// Draw vertical lines
			canvas.drawLine(cellWidth * i + coordinateYSpace, coordinateXSpace, 
					cellWidth * i + coordinateYSpace, gameboardHeight + coordinateXSpace, lightColor);
		}
		
		// Draw major lines
		for (int i=0; i<4; i++) {
			// Draw horizontal lines
			canvas.drawLine(coordinateYSpace, cellHeight * 3 * i + coordinateXSpace, 
					gameboardWidth + coordinateYSpace, cellHeight * 3 * i + coordinateXSpace, darkColor);
			// Draw vertical lines
			canvas.drawLine(cellWidth * 3 * i + coordinateYSpace, coordinateXSpace, 
					cellWidth * 3 * i + coordinateYSpace, gameboardHeight + coordinateXSpace, darkColor);
		}
		
		/* Draw coordinates */
		
		// Set coordinate style
		Paint coordinateStyle = new Paint(Paint.ANTI_ALIAS_FLAG);
		coordinateStyle.setColor(getResources().getColor(R.color.coordinate_color));
		coordinateStyle.setTextSize(cellHeight * 0.2f);
		
		final int ASCII_FOR_A = 65;
		final int ASCII_FOR_ONE = 49;
		
		// Draw horizontal coordinates
		for (int i=0; i<GameVariables.HOUSE_SIZE; i++)
			canvas.drawText(Character.toString (
					(char) (ASCII_FOR_A + i)), cellWidth * i + coordinateYSpace + cellWidth * 0.45f, 
					coordinateXSpace * 0.75f, coordinateStyle);
		
		// Draw vertical coordinates
		for (int i=0; i<GameVariables.HOUSE_SIZE; i++)
			canvas.drawText(Character.toString (
					(char) (ASCII_FOR_ONE + i)), 0.4f * coordinateYSpace, 
					cellHeight * i + coordinateXSpace + cellHeight * 0.55f, coordinateStyle);			
		
		/* Draw clues (numbers) */
		
		// Set clue style
		Paint clueStyle = new Paint(Paint.ANTI_ALIAS_FLAG);
		clueStyle.setColor(getResources().getColor(R.color.clue_color));
		clueStyle.setTextSize(cellHeight * 0.45f);
		clueStyle.setTextAlign(Paint.Align.CENTER);
		
		// Set the coordinate to draw clues in the center of each cell
		float x = cellWidth/2f;
		float y = cellHeight*2/3f;
		
		// Draw clues
		for (int row=0; row<GameVariables.HOUSE_SIZE; row++) {
			for (int col=0; col<GameVariables.HOUSE_SIZE; col++) {
				int index = row * GameVariables.HOUSE_SIZE + col;
				int clue = clues[index];
				if (clue != 0) // there is clue for this cell
					canvas.drawText(Integer.toString(clue), cellWidth * col + x + coordinateXSpace, 
							cellHeight * row + y + coordinateYSpace, clueStyle);
			}
		}
		
		/* Draw pencil marks */
		
		// Set pencil mark style
		Paint pencilmarkStyle = new Paint(Paint.ANTI_ALIAS_FLAG);
		pencilmarkStyle.setColor(getResources().getColor(R.color.clue_color));
		pencilmarkStyle.setTextSize(cellHeight * 0.23f);
		pencilmarkStyle.setTextAlign(Paint.Align.CENTER);
		
		// Set the coordinate to draw pencil marks in the center of each sub-cell
		float px = pencilmarkCellWidth/2f;
		float py = pencilmarkCellHeight*2/3f;
	
		// Draw pencil marks
		for (int row=0; row<GameVariables.HOUSE_SIZE; row++) {
			for (int col=0; col<GameVariables.HOUSE_SIZE; col++) {
				int index = row * GameVariables.HOUSE_SIZE + col;
				for (int pIndex=0; pIndex<GameVariables.HOUSE_SIZE; pIndex++) {
						int pMark = pencilmarks[index][pIndex];
						if (pMark != 0) { // there is pencil mark for this position
							// Determine the position of the pencil mark within a cell
							int pRow = pIndex/3;
							int pCol = pIndex%3;
	
							canvas.drawText(Integer.toString(pMark), 
									cellWidth * col + coordinateXSpace + pencilmarkCellWidth * pCol + px, 
									cellHeight * row + coordinateYSpace + pencilmarkCellHeight * pRow + py, 
									pencilmarkStyle);
						}
				}
			}
		}
	}	
	
	/**
	 * Get the rectangle's coordinate of the selected cell
	 */
	private void getSelectedCell() {		
		selectedCell.set((int)(nCol*cellWidth + coordinateYSpace), (int)(nRow*cellHeight + coordinateXSpace), 
				(int)((nCol + 1)*cellWidth + coordinateYSpace), (int)((nRow + 1)*cellHeight + coordinateXSpace));
	}
	
	private Rect getCell(int rowIndex, int colIndex) {
		Rect cell = new Rect();
		cell.set((int)(colIndex*cellWidth + coordinateYSpace), (int)(rowIndex*cellHeight + coordinateXSpace), 
				(int)((colIndex + 1)*cellWidth + coordinateYSpace), (int)((rowIndex + 1)*cellHeight + coordinateXSpace));
		return cell;
	}
	
	@Override
	public boolean onTouchEvent(MotionEvent event) {
		if (event.getAction() != MotionEvent.ACTION_DOWN)
			return super.onTouchEvent(event);
		
		converTouchCoordToNRowCol(event.getX(), event.getY());
		selectACell();
		Log.d("touch position coordinate:", "(" + event.getX() + ", " + event.getY() + ").");
		
		return true;
		
	}
	
	/**
	 * Determine what cell does the selecting action fall in
	 * @param x		x coordinate of the selecting action
	 * @param y		y coordinate of the selecting action
	 */
	private void converTouchCoordToNRowCol(float x, float y) {
		// calculate which cell the coordinate belongs to
		nRow = (int) ((y - coordinateXSpace)/cellHeight);		
		nCol = (int) ((x - coordinateYSpace)/cellWidth);
		Log.d("nRowCol: ", "(" + nRow + " ," + nCol + ").");		
	}
	

	public void selectACell() {
		getSelectedCell();
		Log.d("selected cell", "cooridnate=" + selectedCell.left + ", " + selectedCell.top + ", " + 
				selectedCell.right + ", " + selectedCell.bottom);
		
		invalidate();
	}
	
	/**
	 * Redraw clues
	 * @param num	the number to be input to the selected cell
	 */
	protected void redrawClues(int[] clues) {
		this.clues = clues;
		
		// Redraw the whole game board
		invalidate();
	}
	
	protected void redrawPencilmarks(int[][] pencilmarks) {
		this.pencilmarks = pencilmarks;
		
		// Redraw the whole game board
		invalidate();		
	}
	
	/**
	 * Get parameter nRow
	 * @return	this.nRow
	 */
	public int getNRow() {
		return this.nRow;
	}
	
	/**
	 * Get parameter nCol
	 * @return this.nCol
	 */
	public int getNCol() {
		return this.nCol;
	}
	
	public void setNRow(int nRow) {
		this.nRow = nRow;
	}
	
	public void setNCol(int nCol) {
		this.nCol = nCol;
	}

}
